/*
Copyright 2007 Brian Tanner
http://rl-library.googlecode.com/
brian@tannerpages.com
http://brian.tannerpages.com

 Licensed under the Apache License, Version 2.0 (the "License");
 you may not use this file except in compliance with the License.
 You may obtain a copy of the License at

     http://www.apache.org/licenses/LICENSE-2.0

 Unless required by applicable law or agreed to in writing, software
 distributed under the License is distributed on an "AS IS" BASIS,
 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 See the License for the specific language governing permissions and
 limitations under the License.
*/
package org.rlcommunity.environments.mountaincar;

import java.util.Random;

/**
 * This class manages all of the problem parameters, current state variables, 
 * and state transition and reward dynamics.
 *
 * @author btanner
 */
public class MountainCarState {
//	Current State Information
    public double position;
    public double velocity;

//Some of these are fixed.  This environment would be easy to parameterize further by changing these.
    public double minPosition = -1.2;
    public double maxPosition = 0.6;
    public double minVelocity = -0.07;
    public double maxVelocity = 0.07;

    public double easyLftRegionMax = -0.6;
    public double easyRtRegionMin = 0.4;
    public double startRegionLftBorder = -0.7;
    public double startRegionRtBorder = -0.3;

    public double goalPosition = 0.5;
    public double accelerationFactor = 0.001;
    public double gravityFactor = -0.0025;
    public double hillPeakFrequency = 3.0;

    //This is the middle of the valley (no slope)
    public double defaultInitPosition = -0.5d;
    public double defaultInitVelocity = 0.0d;
    public double rewardPerStep = -1.0d;
    public double rewardAtGoal = 0.0d;
    private Random randomGenerator;

    //These are configurable
    public boolean randomStarts = false;

    /**
     * Constructor 
     * @param randomGenerator
     */
    MountainCarState(Random randomGenerator) {
        this.randomGenerator = randomGenerator;
    }

    /**
     * Calculate the reward for the 
     * @return
     */
    public double getReward() {
        if (inGoalRegion()) {
            return rewardAtGoal;
        } else {
            return rewardPerStep;
        }
    }


    /**
     * IS the agent past the goal marker?
     * @return
     */
    public boolean inGoalRegion() {
        return position >= goalPosition;
    }

   /**
    * Update the agent's velocity, threshold it, then 
    * update position and threshold it. 
    * @param a Should be in {0 (left), 1 (neutral), 2 (right)}
    */
    void update(int a) {
        double variedAccel = accelerationFactor;

        velocity += ((a - 1)) * variedAccel + getSlope(position) * (gravityFactor);
        if (velocity > maxVelocity) {
            velocity = maxVelocity;
        }
        if (velocity < minVelocity) {
            velocity = minVelocity;
        }
        position += velocity;
        if (position > maxPosition) {
            position = maxPosition;
        }
        if (position < minPosition) {
            position = minPosition;
        }
        if (position == minPosition && velocity < 0) {
            velocity = 0;
        }

    }
/**
 * Get the height of the hill at this position
 * @param queryPosition
 * @return
 */
    public double getHeightAtPosition(double queryPosition) {
        return -Math.sin(hillPeakFrequency * (queryPosition));
    }

/**
 * Get the slop of the hill at this position
 * @param queryPosition
 * @return
 */
    public double getSlope(double queryPosition) {
        /*The curve is generated by cos(hillPeakFrequency(x-pi/2)) so the 
         * pseudo-derivative is cos(hillPeakFrequency* x) 
         */
        return Math.cos(hillPeakFrequency * queryPosition);
    }

    
    /**
     * This is basically a copy constructor and we use it when were doing 
     * env_save_state and env_save_state
     * @param stateToCopy
     */
    public MountainCarState(MountainCarState stateToCopy) {
        this.position = stateToCopy.position;
        this.velocity = stateToCopy.velocity;
        this.minPosition = stateToCopy.minPosition;
        this.maxPosition = stateToCopy.maxPosition;
        this.minVelocity = stateToCopy.minVelocity;
        this.maxVelocity = stateToCopy.maxVelocity;
        this.goalPosition = stateToCopy.goalPosition;
        this.accelerationFactor = stateToCopy.accelerationFactor;
        this.gravityFactor = stateToCopy.gravityFactor;
        this.hillPeakFrequency = stateToCopy.hillPeakFrequency;
        this.defaultInitPosition = stateToCopy.defaultInitPosition;
        this.defaultInitVelocity = stateToCopy.defaultInitVelocity;
        this.rewardPerStep = stateToCopy.rewardPerStep;
        this.rewardAtGoal = stateToCopy.rewardAtGoal;

        this.randomStarts = stateToCopy.randomStarts;

//These are pointers but that's ok
        this.randomGenerator = stateToCopy.randomGenerator;
    }
}
